﻿namespace Bowlus.Common.Communications
{
    using System;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.ServiceModel;

    using Microsoft.WindowsAzure.ServiceRuntime;

    public class CommunicationServiceHost : IDisposable
    {
        // Use rolen name as endpoint name. Bowlus.FrontEnd, or Bowlus.AppHost
        private readonly string serverEndpoint;

        [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation",
            Justification = "Reviewed. Suppression is OK here.")]
        private readonly string ipServerEndpoint;

        private readonly CommunicationService commucationService;

        private ServiceHost serviceHost;

        public CommunicationServiceHost(Action<BowlusMessage> handler)
        {
            this.commucationService = CommunicationService.Instance(handler);

            this.serverEndpoint = RoleEnvironment.CurrentRoleInstance.Role.Name;

            this.ipServerEndpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints[this.serverEndpoint].IPEndpoint.ToString();

            // Hardcode the max retries to 4
            this.StartCommunications(3);
        }

        public void Dispose()
        {
            if (this.serviceHost != null)
            {
                this.serviceHost.Close();
            }
        }

        private void StartCommunications(int retries)
        {
            if (this.commucationService == null)
            {
                throw new ApplicationException("CommunicationService should be not null");
            }

            // recycle the role to see if we can get rid of the faulting condition
            if (retries == 0)
            {
                RoleEnvironment.RequestRecycle();
                return;
            }

            // TODO: use 'new ServiceHost(Type)' instead of 'new ServiceHost(Instance)'
            // We have specified service object initiation behavior in:
            //       [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
            // ServichHost will control instance behavior, and we should give up control on how ServiceHost initiate the service object.
            this.serviceHost = new ServiceHost(this.commucationService);

            this.serviceHost.Faulted += (sender, e) =>
                {
                    Trace.TraceError(
                        string.Format("*** {0}/{1} HOST FAULT. Retrying {2}", this.ipServerEndpoint, this.serverEndpoint, 4 - retries));
                    this.serviceHost.Abort();

                    // Recursively call 
                    this.StartCommunications(--retries);
                };

            var binding = new NetTcpBinding(SecurityMode.None);

            this.serviceHost.AddServiceEndpoint(
                typeof(ICommunicationService),
                binding,
                string.Format("net.tcp://{0}/{1}", this.ipServerEndpoint, this.serverEndpoint));

            try
            {
                this.serviceHost.Open();
                Trace.TraceInformation(
                    string.Format(
                        "*** {0}/{1} Service started.", this.ipServerEndpoint, this.serverEndpoint));
            }
            catch (Exception exception)
            {
                Trace.TraceError(exception.ToString());
            }
        }
    }
}